package com.yhg.backend.websocket;

import com.yhg.backend.config.GetHttpSessionConfig;
import com.yhg.backend.utils.MessageUtils;
import com.yhg.backend.websocket.pojo.Message;
import com.alibaba.fastjson.JSON;
import jakarta.servlet.http.HttpSession;
import jakarta.websocket.*;
import jakarta.websocket.server.ServerEndpoint;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

@ServerEndpoint(value = "/chat", configurator = GetHttpSessionConfig.class)
@Component
public class ChatEndpoint {

    // 保存在线的用户，key为用户名，value为 Session 对象
    private static final Map<String, Session> onlineUsers = new ConcurrentHashMap<>();
    
    // 存储Session与用户名的映射关系，用于在HttpSession为null时识别用户
    private static final Map<Session, String> sessionUserMap = new ConcurrentHashMap<>();
    
    // 获取两个用户之间的消息历史键名（按字母顺序排序）
    private static String getHistoryKey(String user1, String user2) {
        if (user1.compareTo(user2) <= 0) {
            return user1 + ":" + user2;
        } else {
            return user2 + ":" + user1;
        }
    }
    
    // 保存消息到历史记录
    private static void saveMessageToHistory(String fromUser, String toUser, String message) {
        String historyKey = getHistoryKey(fromUser, toUser);
        
        // 如果历史记录不存在，创建一个新的
        messageHistory.computeIfAbsent(historyKey, k -> new ArrayList<>());
        
        List<String> history = messageHistory.get(historyKey);
        
        // 添加消息到历史记录
        history.add(message);
        
        // 如果历史记录超过最大数量，移除最旧的消息
        while (history.size() > MAX_HISTORY_MESSAGES) {
            history.remove(0);
        }
    }
    
    // 获取两个用户之间的消息历史
    private static List<String> getMessageHistory(String user1, String user2) {
        String historyKey = getHistoryKey(user1, user2);
        return messageHistory.getOrDefault(historyKey, new ArrayList<>());
    }
    
    // 清除两个用户之间的聊天记录
    private static void clearChatHistory(String user1, String user2) {
        String historyKey = getHistoryKey(user1, user2);
        // 从消息历史记录中移除该用户对的所有消息
        messageHistory.remove(historyKey);
        System.out.println("已清除用户 '" + user1 + "' 和 '" + user2 + "' 之间的聊天记录");
    }
    
    // 发送历史消息给用户
    private void sendHistoryMessages(String username, Session session) {
        try {
            // 遍历所有在线用户，获取与当前用户相关的历史消息
            for (String otherUser : onlineUsers.keySet()) {
                if (!otherUser.equals(username)) { // 不需要发送与自己的历史消息
                    List<String> history = getMessageHistory(username, otherUser);
                    
                    // 如果有历史消息，发送给当前用户
                    if (!history.isEmpty()) {
                        System.out.println("发送历史消息给用户 '" + username + "'，共 " + history.size() + " 条");
                        
                        // 先发送一个系统消息，表示这是历史消息
                        String systemMsg = MessageUtils.getMessage(true, null, "以下是您与 " + otherUser + " 的历史消息");
                        session.getBasicRemote().sendText(systemMsg);
                        
                        // 发送历史消息
                        for (String msg : history) {
                            session.getBasicRemote().sendText(msg);
                        }
                    }
                }
            }
        } catch (Exception e) {
            System.err.println("发送历史消息时发生异常: " + e.getMessage());
            e.printStackTrace();
        }
    }
    
    // 存储消息历史记录，key为用户名对（格式："user1:user2"，按字母顺序排序），value为消息列表
    private static final Map<String, List<String>> messageHistory = new ConcurrentHashMap<>();
    
    // 每个用户对的最大历史消息数量
    private static final int MAX_HISTORY_MESSAGES = 50;

    private HttpSession httpSession;

    /**
     * 建立websocket连接后，被调用
     *
     * @param session Session
     */
    @OnOpen
    public void onOpen(Session session, EndpointConfig config) {
        try {
            // 获取HttpSession对象
            this.httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName());
            
            // 尝试获取当前用户名
            String username = null;
            
            // 首先尝试从HttpSession获取
            if (this.httpSession != null) {
                username = (String) this.httpSession.getAttribute("currentUser");
                System.out.println("WebSocket连接建立，从HttpSession获取到用户: " + username);
            }
            
            // 如果HttpSession为null或无法获取用户名，尝试从URL参数获取
            if (username == null || username.isEmpty()) {
                Map<String, List<String>> requestParams = session.getRequestParameterMap();
                if (requestParams.containsKey("username")) {
                    List<String> usernameList = requestParams.get("username");
                    if (usernameList != null && !usernameList.isEmpty()) {
                        username = usernameList.get(0);
                        System.out.println("WebSocket连接建立，从URL参数获取到用户: " + username);
                        
                        // 如果从URL获取到了用户名，但HttpSession存在，则将用户名保存到HttpSession中
                        if (this.httpSession != null) {
                            this.httpSession.setAttribute("currentUser", username);
                            System.out.println("已将用户名保存到HttpSession: " + username);
                        }
                    }
                }
            }
            
            // 如果仍然无法获取用户名，记录错误并关闭连接
            if (username == null || username.isEmpty()) {
                System.err.println("错误: 无法确定连接的用户身份");
                session.close(new CloseReason(CloseReason.CloseCodes.VIOLATED_POLICY, "未提供用户名"));
                return;
            }
            
            // 先检查是否已存在相同用户名的连接，如果有则关闭旧连接
            Session existingSession = onlineUsers.get(username);
            if (existingSession != null && existingSession.isOpen() && !existingSession.equals(session)) {
                System.out.println("用户 " + username + " 已有一个活跃连接，关闭旧连接");
                try {
                    // 从映射中移除旧连接
                    sessionUserMap.remove(existingSession);
                    // 关闭旧连接
                    existingSession.close(new CloseReason(CloseReason.CloseCodes.NORMAL_CLOSURE, "用户在其他地方登录"));
                } catch (IOException e) {
                    System.err.println("关闭旧连接时发生错误: " + e.getMessage());
                }
            }
            
            // 将用户的session对象存储到map集合中
            onlineUsers.put(username, session);
            
            // 存储Session与用户名的映射关系
            sessionUserMap.put(session, username);
            
            // 广播消息通知所有用户，当前用户上线
            String message = MessageUtils.getMessage(true, null, getFriends());
            broadcastAllUsers(message);
            
            // 向当前用户发送历史消息
            sendHistoryMessages(username, session);
            
            System.out.println("用户 " + username + " 已连接，当前在线用户数: " + onlineUsers.size());
        } catch (Exception e) {
            System.err.println("WebSocket onOpen方法发生异常: " + e.getMessage());
            e.printStackTrace();
            try {
                session.close(new CloseReason(CloseReason.CloseCodes.UNEXPECTED_CONDITION, "服务器内部错误"));
            } catch (IOException ex) {
                System.err.println("关闭WebSocket连接时发生错误: " + ex.getMessage());
            }
        }
    }


    private Set<String> getFriends() {
        //获取所有用户的集合
        return onlineUsers.keySet();
    }
    //广播方法
    private void broadcastAllUsers(String message) {
        try {
            System.out.println("广播消息给所有用户: " + message);
            //遍历 map 集合
            Set<Map.Entry<String, Session>> entries = onlineUsers.entrySet();
            
            if (entries.isEmpty()) {
                System.out.println("当前没有在线用户，广播消息取消");
                return;
            }
            
            System.out.println("当前在线用户数: " + entries.size());
            
            for (Map.Entry<String, Session> entry : entries) {
                String username = entry.getKey();
                // 获取到所有用户对应的 session 对象
                Session session = entry.getValue();
                
                try {
                    // 发送消息
                    // 使用 getBasicRemote() 方法发送同步消息
                    session.getBasicRemote().sendText(message);
                    System.out.println("成功发送广播消息给用户: " + username);
                } catch (Exception e) {
                    System.err.println("向用户 '" + username + "' 发送广播消息失败: " + e.getMessage());
                }
            }
        } catch (Exception exception) {
            System.err.println("广播消息过程中发生异常: " + exception.getMessage());
            exception.printStackTrace();
        }
    }
    
    /**
     * 群发消息给所有在线用户（除了发送者自己）
     *
     * @param fromUser 发送者用户名
     * @param message 消息内容
     */
    private void broadcastMessage(String fromUser, String message) {
        try {
            System.out.println("用户 '" + fromUser + "' 发送群发消息: " + message);
            
            // 创建群发消息对象，设置toName为"all"表示群发
            String broadcastMessage = MessageUtils.getMessageWithReceiver(false, fromUser, "all", message);
            
            // 遍历所有在线用户的 session 对象
            for (Map.Entry<String, Session> entry : onlineUsers.entrySet()) {
                String username = entry.getKey();
                Session session = entry.getValue();
                
                // 不发送给自己
                if (!username.equals(fromUser)) {
                    try {
                        // 发送消息
                        session.getBasicRemote().sendText(broadcastMessage);
                        System.out.println("成功发送群发消息给用户: " + username);
                    } catch (Exception e) {
                        System.err.println("向用户 '" + username + "' 发送群发消息失败: " + e.getMessage());
                    }
                }
            }
            
            // 给发送者也发送一个确认消息
            Session fromSession = onlineUsers.get(fromUser);
            if (fromSession != null && fromSession.isOpen()) {
                fromSession.getBasicRemote().sendText(broadcastMessage);
            }
            
            // 保存群发消息到历史记录（与每个接收者之间都保存一份）
            for (String username : onlineUsers.keySet()) {
                if (!username.equals(fromUser)) {
                    saveMessageToHistory(fromUser, username, broadcastMessage);
                }
            }
            
        } catch (Exception exception) {
            System.err.println("群发消息过程中发生异常: " + exception.getMessage());
            exception.printStackTrace();
        }
    }

    // 存储Session与用户名的映射关系，用于在HttpSession为null时识别用户
//    private static final Map<Session, String> sessionUserMap = new ConcurrentHashMap<>();
    
    /**
     * 浏览器发送消息到服务端时该方法会被调用，也就是私聊
     * 张三  -->  李四
     *
     * @param message String
     */
    @OnMessage
    public void onMessage(Session session, String message) {
        try {
            // 将消息推送给指定的用户
            Message msg = JSON.parseObject(message, Message.class);
            System.out.println("收到WebSocket消息: " + message);

            // 获取消息接收方的用户名
            String toName = msg.getToName();
            String tempMessage = msg.getMessage();

            // 尝试获取当前用户名
            String currentUser = null;
            
            // 首先尝试从sessionUserMap获取，这是最可靠的来源
            currentUser = sessionUserMap.get(session);
            if (currentUser != null) {
                System.out.println("从sessionUserMap获取到用户名: " + currentUser);
            } else {
                // 如果从sessionUserMap获取失败，尝试从HttpSession获取
                if (this.httpSession != null) {
                    currentUser = (String) this.httpSession.getAttribute("currentUser");
                    System.out.println("从HttpSession获取到用户名: " + currentUser);
                    
                    // 如果从HttpSession获取成功，更新sessionUserMap
                    if (currentUser != null && !currentUser.isEmpty()) {
                        sessionUserMap.put(session, currentUser);
                        System.out.println("已更新sessionUserMap映射: " + session.getId() + " -> " + currentUser);
                    }
                }
                
                // 如果仍然无法获取用户名，尝试从URL参数中获取（这是一个备用方案）
                if (currentUser == null || currentUser.isEmpty()) {
                    Map<String, List<String>> requestParams = session.getRequestParameterMap();
                    if (requestParams.containsKey("username")) {
                        List<String> usernameList = requestParams.get("username");
                        if (usernameList != null && !usernameList.isEmpty()) {
                            currentUser = usernameList.get(0);
                            System.out.println("从URL参数获取到用户名: " + currentUser);
                            
                            // 将用户名保存到sessionUserMap中，以便后续使用
                            if (currentUser != null && !currentUser.isEmpty()) {
                                sessionUserMap.put(session, currentUser);
                                System.out.println("已更新sessionUserMap映射: " + session.getId() + " -> " + currentUser);
                                
                                // 如果HttpSession存在，也将用户名保存到HttpSession中
                                if (this.httpSession != null) {
                                    this.httpSession.setAttribute("currentUser", currentUser);
                                    System.out.println("已将用户名保存到HttpSession: " + currentUser);
                                }
                            }
                        }
                    }
                }
            }
            
            // 如果仍然无法获取用户名，记录错误并返回
            if (currentUser == null) {
                System.err.println("错误: 无法确定发送消息的用户身份");
                return;
            }
            
            // 检查是否是清除聊天记录的命令
            if ("/clear".equals(tempMessage)) {
                // 清除当前用户与接收者之间的聊天记录
                clearChatHistory(currentUser, toName);
                
                // 发送系统消息通知用户聊天记录已清除
                String clearMessage = MessageUtils.getMessage(true, null, "与 " + toName + " 的聊天记录已清除");
                session.getBasicRemote().sendText(clearMessage);
                
                // 如果接收者不是当前用户，也通知接收者
                if (!currentUser.equals(toName)) {
                    String notifyMessage = MessageUtils.getMessage(true, null, currentUser + " 已清除与您的聊天记录");
                    Session receiverSession = onlineUsers.get(toName);
                    if (receiverSession != null && receiverSession.isOpen()) {
                        receiverSession.getBasicRemote().sendText(notifyMessage);
                    }
                }
                
                return;
            }
            
            // 检查是否是群发消息
            if ("all".equals(toName)) {
                // 群发消息给所有在线用户
                broadcastMessage(currentUser, tempMessage);
                return;
            }
            
            // 私聊消息处理
            // 获取消息接收方用户对象的 session 对象
            Session toSession = onlineUsers.get(toName);
            if (toSession == null) {
                System.err.println("错误: 接收方用户 '" + toName + "' 不在线或不存在");
                // 发送系统消息通知用户
                String errorMessage = MessageUtils.getMessage(true, null, "用户 '" + toName + "' 不在线或不存在");
                session.getBasicRemote().sendText(errorMessage);
                return;
            }
            
            System.out.println("发送消息: 从 '" + currentUser + "' 到 '" + toName + "'");
            // 使用新的方法创建带接收者信息的消息
            String messageToSend = MessageUtils.getMessageWithReceiver(false, currentUser, toName, tempMessage);

            // 保存消息到历史记录
            saveMessageToHistory(currentUser, toName, messageToSend);

            // 发送消息给接收者
            toSession.getBasicRemote().sendText(messageToSend);
            
            // 同时发送一个确认消息给发送者（如果发送者不是接收者）
            if (!currentUser.equals(toName)) {
                Session fromSession = onlineUsers.get(currentUser);
                if (fromSession != null && fromSession.isOpen()) {
                    // 创建一个发送确认消息
                    String confirmMessage = MessageUtils.getMessageWithReceiver(false, currentUser, toName, tempMessage);
                    fromSession.getBasicRemote().sendText(confirmMessage);
                }
            }
        } catch (Exception exception) {
            System.err.println("WebSocket onMessage方法发生异常: " + exception.getMessage());
            exception.printStackTrace();
        }
    }

    /**
     * 断开 websocket 连接时被调用
     *
     * @param session Session
     */
    @OnClose
    public void onClose(Session session) {
        try {
            // 尝试获取当前用户名
            String user = null;
            
            // 首先尝试从HttpSession获取
            if (this.httpSession != null) {
                user = (String) this.httpSession.getAttribute("currentUser");
                System.out.println("WebSocket连接关闭，从HttpSession获取到用户: " + user);
            }
            
            // 如果HttpSession为null或无法获取用户名，尝试从sessionUserMap获取
            if (user == null) {
                user = sessionUserMap.get(session);
                System.out.println("WebSocket连接关闭，从sessionUserMap获取到用户: " + user);
            }
            
            if (user != null) {
                // 检查该用户名是否有其他活跃连接
                Session currentSession = onlineUsers.get(user);
                if (currentSession != null && currentSession.equals(session)) {
                    // 只有当要关闭的session是该用户名当前活跃的session时，才从onlineUsers中移除
                    onlineUsers.remove(user);
                    System.out.println("用户 " + user + " 已断开连接，从onlineUsers中移除");
                    
                    // 通知其他用户，当前用户已下线
                    String message = MessageUtils.getMessage(true, null, getFriends());
                    broadcastAllUsers(message);
                } else {
                    System.out.println("用户 " + user + " 的旧连接断开，但有新连接存在，不从onlineUsers中移除");
                }
                
                // 从sessionUserMap中移除当前session
                sessionUserMap.remove(session);
                System.out.println("已从sessionUserMap中移除会话: " + session.getId());
                
                try {
                    if (session.isOpen()) {
                        session.close();
                    }
                } catch (Exception e) {
                    System.err.println("关闭WebSocket会话时发生异常: " + e.getMessage());
                }
                
                System.out.println("用户 " + user + " 已断开连接，当前在线用户数: " + onlineUsers.size());
            } else {
                System.err.println("错误: 无法确定断开连接的用户身份");
                // 尝试从sessionUserMap中移除，即使无法确定用户身份
                sessionUserMap.remove(session);
            }
        } catch (Exception e) {
            System.err.println("WebSocket onClose方法发生异常: " + e.getMessage());
            e.printStackTrace();
            // 尝试从sessionUserMap中移除，即使发生异常
            sessionUserMap.remove(session);
        }
    }
    
    /**
     * WebSocket连接发生错误时调用
     *
     * @param session Session
     * @param error Throwable
     */
    @OnError
    public void onError(Session session, Throwable error) {
        System.err.println("WebSocket连接发生错误");
        error.printStackTrace();
        
        try {
            // 尝试获取当前用户名
            String user = null;
            
            // 首先尝试从HttpSession获取
            if (this.httpSession != null) {
                user = (String) this.httpSession.getAttribute("currentUser");
                System.err.println("WebSocket错误发生时，从HttpSession获取到用户: " + user);
            }
            
            // 如果HttpSession为null或无法获取用户名，尝试从sessionUserMap获取
            if (user == null) {
                user = sessionUserMap.get(session);
                System.err.println("WebSocket错误发生时，从sessionUserMap获取到用户: " + user);
            }
            
            if (user != null) {
                // 从在线用户列表中移除
                onlineUsers.remove(user);
            }
            
            // 从sessionUserMap中移除
            sessionUserMap.remove(session);
            
            if (session != null && session.isOpen()) {
                try {
                    session.close();
                } catch (Exception e) {
                    System.err.println("关闭WebSocket会话时发生异常: " + e.getMessage());
                }
            }
            
            // 如果用户已识别且已移除，通知其他用户更新在线列表
            if (user != null) {
                try {
                    String message = MessageUtils.getMessage(true, null, getFriends());
                    broadcastAllUsers(message);
                    System.out.println("用户 " + user + " 因错误断开连接，当前在线用户数: " + onlineUsers.size());
                } catch (Exception e) {
                    System.err.println("广播用户列表时发生异常: " + e.getMessage());
                }
            }
        } catch (Exception e) {
            System.err.println("处理WebSocket错误时发生异常: " + e.getMessage());
            e.printStackTrace();
        }
    }

}